/*
 * This file is part of Smart Route for S60 3rd Edition terminals
 * 
 * Copyright (C) 2006  Harri Salminen

 * This program free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 *(at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
 */


#include "SmartRouteDataStore.h"
#include <aknutils.h>

CSmartRoute::CSmartRoute() 
	{
	// No implementation required
	}


CSmartRoute::~CSmartRoute()
	{
	delete iName;
	iRoutePoints.Close();
	}

CSmartRoute* CSmartRoute::NewL()
	{
	CSmartRoute* self= new (ELeave)CSmartRoute;
	CleanupStack::PushL(self);
	self->ConstructL();
	CleanupStack::Pop(); // self;
	return self;
	}
	
void CSmartRoute::ConstructL()
	{
	}

void CSmartRoute::SetNameL(const TDesC& aName)
	{
	delete iName;
	iName = NULL;
	iName = aName.AllocL();
	}
	
const TDesC& CSmartRoute::Name()
	{
	if(!iName)
		{
		TBuf<KMaxShortDateFormatSpec*2> buffer;
		iStart.FormatL(buffer,TShortDateFormatSpec());
		iName = buffer.Alloc();
		}
		
	if(iName)
		return *iName;
	else
		return KNullDesC();
	}

void CSmartRoute::SetStart(const TTime& aStart)
	{
	iStart = aStart;
	}
	
const TTime& CSmartRoute::Start() const
	{
	return iStart;
	}
	
void CSmartRoute::SetEnd(const TTime& aEnd)
	{
	iEnd = aEnd;
	}
		
const TTime& CSmartRoute::End() const
	{
	return iEnd;
	}

void CSmartRoute::SetMaxSpeed(TReal32 aMaxSpeed)
	{
	iMaxSpeed = aMaxSpeed;
	}
	
const TReal32& CSmartRoute::MaxSpeed() const
	{
	return iMaxSpeed;
	}
		
void CSmartRoute::SetDistance(TReal32 aDistance)
	{
	iDistance = aDistance;
	}

const TReal32& CSmartRoute::Distance() const
	{
	return iDistance;
	}
			
void CSmartRoute::AddPointL(const TRoutePoint& aPoint)
	{
	iRoutePoints.AppendL(aPoint);
	}

const RArray<TRoutePoint>& CSmartRoute::Points() const
	{
	return iRoutePoints;
	}
	
void CSmartRoute::SetId(const TUint32& aId)
	{
	iId = aId;
	}
	
TUint32 CSmartRoute::Id() const
	{
	return iId;
	}
	
void CSmartRoute::SetDuration(TInt32 aDuration)
	{
	iDuration = aDuration;
	}
	
const TInt32& CSmartRoute::Duration() const
	{
	return iDuration;
	}
	
void CSmartRoute::ExportL(const TDesC& aFileName, const TFileExportType aType)
	{
	}
	
/////

CSmartRouteDataStore::CSmartRouteDataStore()
	{ 
	// No implementation required
	}


CSmartRouteDataStore::~CSmartRouteDataStore()
	{
	iDatabase.Close();
	}

CSmartRouteDataStore* CSmartRouteDataStore::NewL()
	{
	CSmartRouteDataStore* self= new (ELeave)CSmartRouteDataStore;
	CleanupStack::PushL(self);
	self->ConstructL();
	CleanupStack::Pop(); // self;
	return self;
	}

void CSmartRouteDataStore::ConstructL()
	{
	TInt status;
	
	TFileName databaseFile;  
	#ifdef __WINS__
	databaseFile = KDatabaseFileEmulator;
	#else
	databaseFile = KDatabaseFile;
	CompleteWithAppPath(databaseFile);
	#endif
	
	status = CEikonEnv::Static()->FsSession().MkDirAll(databaseFile);
	if(status != KErrNone && status != KErrAlreadyExists)
		User::Leave(status);
		
	status = iDatabase.Open(CEikonEnv::Static()->FsSession(),databaseFile);
	switch(status)
		{
		case KErrNone:
			break;
		case KErrNotFound:
			status = iDatabase.Create(CEikonEnv::Static()->FsSession(),databaseFile);
			User::LeaveIfError(status);
			CreateTablesL();
			break;
		default:
			User::Leave(status);
		}
	}

void CSmartRouteDataStore::CreateTablesL()
	{
	//Route points
	TDbCol altitude(KAltitudeCol, EDbColReal32); 
	TDbCol latitude(KLatitudeCol, EDbColReal64); 
	TDbCol longitude(KLongitudeCol, EDbColReal64); 
	TDbCol route(KRouteCol, EDbColUint32);  // Reference to route
	TDbCol id(KIdCol, EDbColUint32);
	id.iAttributes = TDbCol::EAutoIncrement;
	
	CDbColSet* pointColSet = CDbColSet::NewLC();
	pointColSet->AddL(altitude);
	pointColSet->AddL(latitude);
	pointColSet->AddL(longitude);
	pointColSet->AddL(route);
	pointColSet->AddL(id);
	User::LeaveIfError(iDatabase.CreateTable(KPointTable, *pointColSet));
	CleanupStack::PopAndDestroy(); // pointColSet
	
	CDbKey* index = CDbKey::NewLC();
	TDbKeyCol routeCol(KRouteCol);
	index->AddL(routeCol);
	User::LeaveIfError(iDatabase.CreateIndex(
	KPointIndex, KPointTable, *index));
	CleanupStack::PopAndDestroy(index);
	index = NULL;
	
	//Route
	TDbCol name(KRouteNameCol, EDbColText,255); 
	TDbCol start(KRouteStartCol, EDbColInt64); 
	TDbCol stop(KRouteEndCol, EDbColInt64);
	TDbCol maxSpeed(KRouteMaxSpeedCol, EDbColReal32);
	TDbCol distance(KRouteDistanceCol, EDbColReal32);
	TDbCol duration(KRouteDurationCol, EDbColInt32);
	
	CDbColSet* routeColSet = CDbColSet::NewLC();
	routeColSet->AddL(name);
	routeColSet->AddL(id);
	routeColSet->AddL(start);
	routeColSet->AddL(stop);
	routeColSet->AddL(maxSpeed);
	routeColSet->AddL(distance);
	routeColSet->AddL(duration);
	
	User::LeaveIfError(iDatabase.CreateTable(KRouteTable, *routeColSet));
	CleanupStack::PopAndDestroy(); // routeColSet
	
	index = CDbKey::NewLC();
	TDbKeyCol idCol(KIdCol);
	index->AddL(idCol);
	User::LeaveIfError(iDatabase.CreateIndex(
	KRouteIndex, KRouteTable, *index));
	CleanupStack::PopAndDestroy(index);
	}

CSmartRoute* CSmartRouteDataStore::RestoreRouteL(const TUint32 aRouteId)
	{
	CSmartRoute* route = CSmartRoute::NewL();
	CleanupStack::PushL(route);
	
	TDbSeekKey seekKey((TUint)aRouteId);
	
	RDbTable routeTable, pointTable;
	User::LeaveIfError(routeTable.Open(iDatabase,KRouteTable, routeTable.EUpdatable));
	CleanupClosePushL(routeTable);
	
	User::LeaveIfError(pointTable.Open(iDatabase,KPointTable, pointTable.EUpdatable));
	CleanupClosePushL(pointTable);
	
	User::LeaveIfError(pointTable.SetIndex(KPointIndex));
	User::LeaveIfError(routeTable.SetIndex(KRouteIndex));
	
	if(routeTable.SeekL(seekKey))
		{
		routeTable.GetL();
		CDbColSet* routeColSet = routeTable.ColSetL();
		CleanupStack::PushL(routeColSet);
		
		route->SetNameL(routeTable.ColDes(routeColSet->ColNo(KRouteNameCol)));
		route->SetId(routeTable.ColUint32(routeColSet->ColNo(KIdCol)));
		route->SetStart(TTime(routeTable.ColInt64(routeColSet->ColNo(KRouteStartCol))));
		route->SetEnd(TTime(routeTable.ColInt64(routeColSet->ColNo(KRouteEndCol))));
		route->SetMaxSpeed(routeTable.ColReal32(routeColSet->ColNo(KRouteMaxSpeedCol)));
		route->SetDistance(routeTable.ColReal32(routeColSet->ColNo(KRouteDistanceCol)));
		route->SetDuration(routeTable.ColInt32(routeColSet->ColNo(KRouteDurationCol)));
		CleanupStack::PopAndDestroy(); //routeColSet
		
		CDbColSet* pointColSet = pointTable.ColSetL();
		CleanupStack::PushL(pointColSet);
		TBool isMore = pointTable.SeekL(seekKey);
		TUint32 routeId;
		while(isMore)
			{
			pointTable.GetL();
			routeId = pointTable.ColUint32(pointColSet->ColNo(KRouteCol));
			if(routeId != route->Id())
				break;
				
			TRoutePoint point;
			point.iAltitude = pointTable.ColReal32(pointColSet->ColNo(KAltitudeCol));
			point.iLatitude = pointTable.ColReal64(pointColSet->ColNo(KLatitudeCol));
			point.iLongitude = pointTable.ColReal64(pointColSet->ColNo(KLongitudeCol));
			route->AddPointL(point);
			isMore = pointTable.NextL();
			}
		CleanupStack::PopAndDestroy(); //pointColSet
		}
	else
		User::Leave(KErrNotFound);
	
	CleanupStack::PopAndDestroy(2); //routeTable, pointTable
	CleanupStack::Pop(); //route
	return route;
	}
	
void CSmartRouteDataStore::SaveRouteL(CSmartRoute& aRoute)
	{
	RDbTable routeTable, pointTable;
	User::LeaveIfError(routeTable.Open(iDatabase,KRouteTable, routeTable.EUpdatable));
	CleanupClosePushL(routeTable);
	
	User::LeaveIfError(pointTable.Open(iDatabase,KPointTable, pointTable.EUpdatable));
	CleanupClosePushL(pointTable);
	
	CDbColSet* routeColSet = routeTable.ColSetL();
	CleanupStack::PushL(routeColSet);
	
	CDbColSet* pointColSet = pointTable.ColSetL();
	CleanupStack::PushL(pointColSet);
	
	routeTable.Reset();
	routeTable.InsertL();
	routeTable.SetColL(routeColSet->ColNo(KRouteNameCol), aRoute.Name());
	routeTable.SetColL(routeColSet->ColNo(KRouteStartCol), aRoute.Start().Int64());
	routeTable.SetColL(routeColSet->ColNo(KRouteEndCol), aRoute.End().Int64());
	routeTable.SetColL(routeColSet->ColNo(KRouteMaxSpeedCol), aRoute.MaxSpeed());
	routeTable.SetColL(routeColSet->ColNo(KRouteDistanceCol), aRoute.Distance());
	routeTable.SetColL(routeColSet->ColNo(KRouteDurationCol), aRoute.Duration());
	
	routeTable.PutL();
	
	TUint32 routeId = routeTable.ColUint32(routeColSet->ColNo(KIdCol));
	
	TDbColNo altitudeCol = pointColSet->ColNo(KAltitudeCol);
	TDbColNo latitudeCol = pointColSet->ColNo(KLatitudeCol);
	TDbColNo longitudeCol = pointColSet->ColNo(KLongitudeCol);
	TDbColNo routeCol = pointColSet->ColNo(KRouteCol);
	
	const RArray<TRoutePoint>& points = aRoute.Points();
	TRoutePoint point;
	for(TInt i=0; i<points.Count(); i++)
		{
		point = points[i];
		pointTable.Reset();
		pointTable.InsertL();
		
		pointTable.SetColL(altitudeCol,point.iAltitude);
		pointTable.SetColL(latitudeCol,point.iLatitude);
		pointTable.SetColL(longitudeCol,point.iLongitude);
		pointTable.SetColL(routeCol,routeId);
		
		pointTable.PutL();
		}
	
	CleanupStack::PopAndDestroy(4); //routeTable, pointTable, routeColSet, pointColSet
	}

void CSmartRouteDataStore::DeleteRouteL(const TUint32 aRoute)
	{
	TDbSeekKey seekKey((TUint)aRoute);
	
	RDbTable routeTable, pointTable;
	User::LeaveIfError(routeTable.Open(iDatabase,KRouteTable, routeTable.EUpdatable));
	CleanupClosePushL(routeTable);
	
	User::LeaveIfError(pointTable.Open(iDatabase,KPointTable, pointTable.EUpdatable));
	CleanupClosePushL(pointTable);
	
	User::LeaveIfError(pointTable.SetIndex(KPointIndex));
	User::LeaveIfError(routeTable.SetIndex(KRouteIndex));
	
	while(routeTable.SeekL(seekKey))
		{
		routeTable.DeleteL();
		}
		
	while(pointTable.SeekL(seekKey))
		{
		pointTable.DeleteL();
		}
	
	CleanupStack::PopAndDestroy(2); //routeTable, pointTable
	}
	
void CSmartRouteDataStore::ListRoutesL(RShortRouteArray& aRouteList)
	{
	RDbTable routeTable;
	User::LeaveIfError(routeTable.Open(iDatabase,KRouteTable, routeTable.EUpdatable));
	CleanupClosePushL(routeTable);
	User::LeaveIfError(routeTable.SetIndex(KRouteIndex));
	
	CDbColSet* routeColSet = routeTable.ColSetL();
	CleanupStack::PushL(routeColSet);
	
	TBool atRow = routeTable.FirstL();
	while(atRow)
		{
		TShortRoute route;
		routeTable.GetL();
		route.iShortName = routeTable.ColDes(routeColSet->ColNo(KRouteNameCol)).Left(KShortNameLen);
		route.iId = routeTable.ColUint32(routeColSet->ColNo(KIdCol));
		route.iStart = TTime(routeTable.ColInt64(routeColSet->ColNo(KRouteStartCol)));
		route.iEnd = TTime(routeTable.ColInt64(routeColSet->ColNo(KRouteEndCol)));
		aRouteList.Append(route); 
		atRow = routeTable.NextL();
		}
	
	CleanupStack::PopAndDestroy(2); //routeTable, routeColSet
	}